package com.huangsm.book.designmodel.nio;


import sun.nio.ch.DirectBuffer;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * Buffer的相关操作
 *
 * @author huang
 * @PACKAGE_NAME com.huangsm.book.designmodel.nio
 * @PROJECT_NAME concurrence
 * @date 2019/2/18
 */
public class BufferOperation {

    public ByteBuffer createByffer(int capacity) {
        //从堆中分配
        ByteBuffer byteBuffer = ByteBuffer.allocate(capacity);

        //从既有数组中创建
        byte[] bytes = new byte[capacity];
        ByteBuffer bufferWarp = ByteBuffer.wrap(bytes);
        return byteBuffer;
    }

    /**
     * 重置和清空缓冲区
     *  这三个行数具有类似的功能，他们都重置了Buffer对象。这里所谓的重置，
     *  只是指重置了Buffer的各项标志位，并不是真正清空Buffer的内容。3个函数的功能
     *  也有所不同。
     * @param byteBuffer
     */
    public void rewind(ByteBuffer byteBuffer) {
        /**
         * 将position重置为零，并清空标志位mark。它的作用在于为提取Buffer的有效数据做准备。
         */
        //从Buffer读取数据写入Channel
        //out.write(byteBuffer);
        //回滚Buffer
        byteBuffer.rewind();
        //将Buffer的有效数据复制到数组中
        //byteBuffer.get(array);


        /**
         *  函数clear也将position置零，同时将limit设置为capacity的大小，并清除标志
         *  mark。由于清空了limit，因此无法得知Buffer内那些数据是真实有效的。这个方法用于
         *  为重新写Buffer做准备。
         */
        byteBuffer.clear(); //为读入数据到Buffer做准备
        //in.read(Buffer); 从通道读入数据

        /**
         * 函数flip()先将limit设置到position所在位置，然后将position置零，并清除标志位
         * mark。它通常在读写转换时使用
         */
        byte [] magic=new byte[10];
        byteBuffer.put(magic);//将magic数组写入Buffer
        //in.read(byteBuffer) 从通道读入给定信息，存放到Buffer中
        byteBuffer.flip();//将Buffer从写状态转为读状态
        //out.write(buffer) 将magic和in通道中写入的信息，写到通道out中

    }

    /**
     * mark和reset的使用
     * @param byteBuffer
     * @return
     */
    public static void useMark(ByteBuffer byteBuffer){
        for (int i = 0; i < 10; i++) {
            byteBuffer.put((byte) i);
        }
        byteBuffer.flip();//写状态转换为读状态
        for (int i = 0; i < byteBuffer.limit(); i++) {
            System.out.println(byteBuffer.get());
            //在第四个位置坐mark
            if (i==4){
                byteBuffer.mark();
                System.out.println("(mark at "+i+")");
            }
        }
        //回到mark的位置，并处理后续数据
        byteBuffer.reset();
        System.out.println("\nreset to mark");
        while (byteBuffer.hasRemaining()){//后续所有数据都将被处理
            System.out.println(byteBuffer.get());
        }
    }

    /**
     * 复制缓冲区
     * duplicate()
     * @param byteBuffer
     */
    public static void useDuplicate(ByteBuffer byteBuffer){
        for (int i = 0; i < 10; i++) {
            byteBuffer.put((byte) i);
        }
        //复制当前缓冲区
        ByteBuffer c = byteBuffer.duplicate();
        System.out.println("After byteBuffer.duplicate()");
        System.out.println(byteBuffer);
        System.out.println(c);
        c.flip();//重置缓冲区c
        System.out.println("After c.flip()");
        System.out.println(byteBuffer);
        System.out.println(c);
        c.put((byte) 100);//向c缓冲区存数据
        System.out.println("After c.put()");
        System.out.println(byteBuffer.get(0));
        System.out.println(c.get(0));
    }

    /**
     * 分割缓冲区
     * @param byteBuffer
     * @return
     */
    public static void useSlice(ByteBuffer byteBuffer){
        for (int i = 0; i < 10; i++) {
            byteBuffer.put((byte) i);
        }
        /**
         * 取位置2-6的数据
         */
        byteBuffer.position(2);
        byteBuffer.limit(6);
        ByteBuffer subBuffer = byteBuffer.slice();
        //在子缓冲区进行操作
        for (int i = 0; i < subBuffer.capacity(); i++) {
            byte bb = subBuffer.get(i);
            bb*=10;
            subBuffer.put(i,bb);
        }
        //设置位置为0
        byteBuffer.position(0);
        byteBuffer.limit(byteBuffer.capacity());
        while (byteBuffer.hasRemaining()){
            System.out.println(byteBuffer.get()+" ");
        }
    }

    /**
     * 只读缓冲区
     * @param byteBuffer
     */
    public static void onlyRead(ByteBuffer byteBuffer){
        for (int i = 0; i < 10; i++) {
            byteBuffer.put((byte) i);
        }
        //创建只读缓冲区
        ByteBuffer readOnly = byteBuffer.asReadOnlyBuffer();
        readOnly.flip();
        while (readOnly.hasRemaining()){
            System.out.println(readOnly.get());
        }
        System.out.println();
        byteBuffer.put(2, (byte) 20);
      //  readOnly.put(2, (byte) 20);//抛出异常java.nio.ReadOnlyBufferException
        readOnly.flip();
        while (readOnly.hasRemaining()){
            System.out.println(readOnly.get());
        }
    }

    /**
     * 文件映射到内存
     */
    public static void fileChannel(){
        RandomAccessFile raf= null;
        try {
            raf = new RandomAccessFile("C:\\Users\\h1261\\Downloads\\niotest.txt","rw");
            FileChannel fc=raf.getChannel();
            MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, raf.length());
            while (mbb.hasRemaining()){
                System.out.println(mbb.get());
            }
            mbb.put(0, (byte) 98);
            raf.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     *
     * @param bookNameStr
     * @param author
     * @throws Exception
     */
    public static void structuring(String bookNameStr,String author) throws Exception {
        //通过聚集写操作创建该文件
        ByteBuffer bookBuf = ByteBuffer.wrap(bookNameStr.getBytes("utf-8"));
        ByteBuffer autBuf = ByteBuffer.wrap(author.getBytes("utf-8"));
        int boolen = bookBuf.limit(); //记录书名长度
        int autlen = autBuf.limit(); //记录作者名字长度
        ByteBuffer[] bufs = {bookBuf, autBuf};
        File file=new File("C:\\Users\\h1261\\Downloads\\读书感悟huangsm.txt");
        if (!file.exists()){
            file.createNewFile();
        }
        FileOutputStream fos = new FileOutputStream(file);
        FileChannel fc = fos.getChannel();
        fc.write(bufs);
        fc.close();
        /**
         * 首先创建俩个ByteBuffer，分别存放书名和作者信息。构造ByteBuffer数组，使用文件通道的聚集写将数组写入文件。程序运行结束
         * 后，便会在指定位置生成新的文件。
         */
        //使用散射读操作，将该文件解析成书名、作者俩个字符串
        ByteBuffer b1 = ByteBuffer.allocate(boolen);
        ByteBuffer b2 = ByteBuffer.allocate(autlen);
        ByteBuffer[] buffers = {b1, b2};
        FileInputStream fis = new FileInputStream(file);
        FileChannel fisChannel = fis.getChannel();
        fisChannel.read(buffers);
        String bookname = new String(buffers[0].array(), "utf-8");
        String autname = new String(buffers[1].array(), "utf-8");
        System.out.println(bookname+autname);

    }

    /**
     * 创建DirectBuffer
     */
    public void getDirectBuffer(){
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(10);
    }

    /**
     * 监控DirectBuffer的使用情况
     */
    public void monDirectBuffer() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        //通过反射得到私有数据
        Class c = Class.forName("java.nio.Bits");
        Field maxMemory = c.getDeclaredField("maxMemory");
        //开启可访问性
        maxMemory.setAccessible(true);
        Field reservedMemory = c.getDeclaredField("reservedMemory");
        reservedMemory.setAccessible(true);
        synchronized (c){
            //总大小
            Long maxMemoryValue = (Long) maxMemory.get(null);
            //剩余大小
            Long reservedMemoryValue= (Long) reservedMemory.get(null);
            System.out.println("maxMemoryValue"+maxMemoryValue);
            System.out.println("reservedMemoryValue"+reservedMemoryValue);
        }
    }
    public static void main(String[] args) {
        ByteBuffer allocate = ByteBuffer.allocate(15);
        //useMark(allocate);
        //useDuplicate(allocate);
        //useSlice(allocate);
        //onlyRead(allocate);
        //fileChannel();
        try {
            structuring("我的读后感","huangsm");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
